package com.orange.ui.launcher;


import com.orange.audio.music.MusicManager;
import com.orange.audio.sound.ISoundManager;
import com.orange.engine.Engine;
import com.orange.engine.options.EngineOptions;
import com.orange.entity.scene.group.SceneGroup;
import com.orange.event.EventDispatcher;
import com.orange.opengl.shader.ShaderProgramManager;
import com.orange.opengl.texture.TextureManager;
import com.orange.opengl.util.GLState;
import com.orange.opengl.vbo.VertexBufferObjectManager;
import com.orange.opengl.view.IRendererListener;
import com.orange.res.ResManager;
import com.orange.ui.IExitGame;
import com.orange.ui.IGameInterface;
import com.orange.util.debug.Debug;

/**
 * (c) 2010 
 * (c) 2011 
 * 
 * @author 
 * @since 11:27:06 - 08.03.2010
 */
public abstract class BaseGameLauncher extends ILauncher implements IGameInterface, IRendererListener {
	// ===========================================================
	// Constants
	// ===========================================================

	// ===========================================================
	// Fields
	// ===========================================================

	protected Engine mEngine;


	private boolean mGamePaused;
	private boolean mGameCreated;
	private boolean mCreateGameCalled;
	private boolean mOnReloadResourcesScheduled;
	
	protected IExitGame iExitGame;

	// ===========================================================
	// Constructors
	// ===========================================================

	
	public void Create() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onCreate" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		this.mGamePaused = true;
		this.mEngine = this.onCreateEngine(this.onCreateEngineOptions());
		this.mEngine.startUpdateThread();
		
		ResManager.init(this.mEngine);
		EventDispatcher.init(this.mEngine);

	}
	
	
	public Engine onCreateEngine(final EngineOptions pEngineOptions) {
		Engine engine = new Engine(pEngineOptions);
		engine.setBaseGameLauncher(this);
		return engine;
	}

	
	public synchronized void onSurfaceCreated(final GLState pGLState) {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onSurfaceCreated" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		if(this.mGameCreated) {
			this.onReloadResources();

			if(this.mGamePaused && this.mGameCreated) {
//				this.onResumeGame();
			}
		} else {
			if(this.mCreateGameCalled) {
				this.mOnReloadResourcesScheduled = true;
			} else {
				this.mCreateGameCalled = true;
				this.onCreateGame();
			}
		}
	}

	
	public synchronized void onSurfaceChanged(final GLState pGLState, final int pWidth, final int pHeight) {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onSurfaceChanged(Width=" + pWidth + ",  Height=" + pHeight + ")" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}
	}

	protected synchronized void onCreateGame() {
		if(Debug.DEBUG) {
//			Debug.d(this.getClass().getSimpleName() + ".onCreateGame" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		final OnPopulateSceneCallback onPopulateSceneCallback = new OnPopulateSceneCallback() {
			
			public void onPopulateSceneFinished() {
				try {
					if(Debug.DEBUG) {
//						Debug.d(BaseGameActivity.this.getClass().getSimpleName() + ".onGameCreated" + " @(Thread: '" + Thread.currentThread().getName() + "')");
					}

					BaseGameLauncher.this.onGameCreated();
				} catch(final Throwable pThrowable) {
					Debug.e(BaseGameLauncher.this.getClass().getSimpleName() + ".onGameCreated failed." + " @(Thread: '" + Thread.currentThread().getName() + "')", pThrowable);
				}

//				BaseGameActivity.this.callGameResumedOnUIThread();
				BaseGameLauncher.this.onResumeGame();
			}
		};

		final OnCreateSceneCallback onCreateSceneCallback = new OnCreateSceneCallback() {
			
			public void onCreateSceneFinished(final SceneGroup pSceneGroup) {
				BaseGameLauncher.this.mEngine.setSceneGroup(pSceneGroup);

				try {
					if(Debug.DEBUG) {
//						Debug.d(BaseGameActivity.this.getClass().getSimpleName() + ".onPopulateScene" + " @(Thread: '" + Thread.currentThread().getName() + "')");
					}

					BaseGameLauncher.this.onPopulateScene(pSceneGroup, onPopulateSceneCallback);
				} catch(final Throwable pThrowable) {
					Debug.e(BaseGameLauncher.this.getClass().getSimpleName() + ".onPopulateScene failed." + " @(Thread: '" + Thread.currentThread().getName() + "')", pThrowable);
				}
			}
		};

		final OnCreateResourcesCallback onCreateResourcesCallback = new OnCreateResourcesCallback() {
			
			public void onCreateResourcesFinished() {
				try {
					if(Debug.DEBUG) {
//						Debug.d(BaseGameActivity.this.getClass().getSimpleName() + ".onCreateScene" + " @(Thread: '" + Thread.currentThread().getName() + "')");
					}

					BaseGameLauncher.this.onCreateScene(onCreateSceneCallback);
				} catch(final Throwable pThrowable) {
					Debug.e(BaseGameLauncher.this.getClass().getSimpleName() + ".onCreateScene failed." + " @(Thread: '" + Thread.currentThread().getName() + "')", pThrowable);
				}
			}
		};

		try {
			if(Debug.DEBUG) {
//				Debug.d(this.getClass().getSimpleName() + ".onCreateResources" + " @(Thread: '" + Thread.currentThread().getName() + "')");
			}

			this.onCreateResources(onCreateResourcesCallback);
		} catch(final Throwable pThrowable) {
			Debug.e(this.getClass().getSimpleName() + ".onCreateGame failed." + " @(Thread: '" + Thread.currentThread().getName() + "')", pThrowable);
		}
	}

	
	public synchronized void onGameCreated() {
		this.mGameCreated = true;

		/* Since the potential asynchronous resource creation,
		 * the surface might already be invalid
		 * and a resource reloading might be necessary. */
		if(this.mOnReloadResourcesScheduled) {
			this.mOnReloadResourcesScheduled = false;
			try {
				this.onReloadResources();
			} catch(final Throwable pThrowable) {
				Debug.e(this.getClass().getSimpleName() + ".onReloadResources failed." + " @(Thread: '" + Thread.currentThread().getName() + "')", pThrowable);
			}
		}
	}

	
	public synchronized void Resume() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".Resume" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}
		doResumeGame();
	}

	public synchronized void doResumeGame() {
		this.onResumeGame();
	}
	
	
	public synchronized void onResumeGame() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onResumeGame" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		this.mEngine.start();

		this.mGamePaused = false;
	}

	
	public void onReloadResources() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onReloadResources" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		this.mEngine.onReloadResources();
	}

	
	public synchronized void Pause() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".Pause" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}
		doPauseGame();
	}
	
	/**
	 * 暂停游戏
	 */
	public synchronized void doPauseGame(){
		if(!this.mGamePaused) {
			this.onPauseGame();
		}
	}

	
	public synchronized void onPauseGame() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onPauseGame" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		this.mGamePaused = true;

		this.mEngine.stop();
	}

	
	public void Destroy() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onDestroy" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		this.mEngine.onDestroy();

		try {
			this.onDestroyResources();
		} catch (final Throwable pThrowable) {
			Debug.e(this.getClass().getSimpleName() + ".onDestroyResources failed." + " @(Thread: '" + Thread.currentThread().getName() + "')", pThrowable);
		}

		this.onGameDestroyed();

		this.mEngine = null;
	}

	
	public void onDestroyResources() throws Exception {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onDestroyResources" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		if(this.mEngine.getEngineOptions().getAudioOptions().needsMusic()) {
			this.getMusicManager().releaseAll();
		}

		if(this.mEngine.getEngineOptions().getAudioOptions().needsSound()) {
			this.getSoundManager().releaseAll();
		}
	}

	
	public synchronized void onGameDestroyed() {
		if(Debug.DEBUG) {
			Debug.d(this.getClass().getSimpleName() + ".onGameDestroyed" + " @(Thread: '" + Thread.currentThread().getName() + "')");
		}

		this.mGameCreated = false;
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	public Engine getEngine() {
		return this.mEngine;
	}
	
	public void setiExitGame(IExitGame iExitGame) {
		this.iExitGame = iExitGame;
	}

	public boolean isGamePaused() {
		return this.mGamePaused;
	}

	public boolean isGameRunning() {
		return !this.mGamePaused;
	}

	public boolean isGameLoaded() {
		return this.mGameCreated;
	}

	public VertexBufferObjectManager getVertexBufferObjectManager() {
		return this.mEngine.getVertexBufferObjectManager();
	}

	public TextureManager getTextureManager() {
		return this.mEngine.getTextureManager();
	}

//	public FontManager getFontManager() {
//		return this.mEngine.getFontManager();
//	}

	public ShaderProgramManager getShaderProgramManager() {
		return this.mEngine.getShaderProgramManager();
	}

	public ISoundManager getSoundManager() {
		return this.mEngine.getSoundManager();
	}

	public MusicManager getMusicManager() {
		return this.mEngine.getMusicManager();
	}

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	// ===========================================================
	// Methods
	// ===========================================================

	/**
	 * @see Engine#runOnUpdateThread(Runnable)
	 */
	public void runOnUpdateThread(final Runnable pRunnable) {
		this.mEngine.runOnUpdateThread(pRunnable);
	}

	/**
	 * @see Engine#runOnUpdateThread(Runnable, boolean)
	 */
	public void runOnUpdateThread(final Runnable pRunnable, final boolean pOnlyWhenEngineRunning) {
		this.mEngine.runOnUpdateThread(pRunnable, pOnlyWhenEngineRunning);
	}


	public Boolean onKeyDown(int keyCode) {
		// TODO Auto-generated method stub
		return null;
	}


	public Boolean onKeyUp(int keyCode) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public void finishGame(){
		iExitGame.onExitGame();
	}


	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================
}
